function isFunction(val) {
  return Object.prototype.toString.call(val) === '[object Function]'
}

function isObject(val) {
  return Object.prototype.toString.call(val) === '[object Object]'
}

function isArray(val) {
  return Object.prototype.toString.call(val) === '[object Array]'
}

function isSet(val) {
  return Object.prototype.toString.call(val) === '[object Set]'
}

function isMap(val) {
  return Object.prototype.toString.call(val) === '[object Map]'
}

function isSymbol(val) {
  return Object.prototype.toString.call(val) === '[object Symbol]'
}

function isDate(val) {
  return Object.prototype.toString.call(val) === '[object Date]'
}

function ArrayBuffer(val) {
  return Object.prototype.toString.call(val) === '[object ArrayBuffer]'
}

const forEachValue = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key))

function deepClone(val, weakMap = new WeakMap()) {
  if (isDate(val)) return new Date(+val)

  if (isMap(val)) {
    const map = new Map()
    for (const item of val) map.set(deepClone(item[0], weakMap), deepClone(item[1], weakMap))
    return map
  }

  if (isSet(val)) {
    const set = new Set()
    val.forEach(item => set.add(deepClone(item), weakMap))
    return set
  }

  if (isSymbol(val)) return Symbol(val.description)

  if (isFunction(val)) {
    if (/^function|^\(\)/.test(val.toString())) {
      return new Function(`return ${val.toString()}`)()
    } else {
      return new Function(`return function ${val.toString()}`)()
    }
  }

  if (!isObject(val)) return val

  const obj = isArray(val) ? [] : {}

  if (weakMap.has(val)) return weakMap.get(val)

  weakMap.set(val, obj)
  forEachValue(val, (val, key) => obj[key] = deepClone(val, weakMap))

  const symbols = Object.getOwnPropertySymbols(val)
  forEachValue(symbols, key => obj[Symbol(key.description)] = deepClone(symbols[key], weakMap))

  return obj
}
/**
 * 验证URL格式
 */
export function urlRegExp(value) {
  return /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/.test(value)
}
/**
 * JS对象深度合并
 */
export function deepMerge(target = {}, source = {}) {
  target = deepClone(target);
  if (typeof target !== 'object' || typeof source !== 'object') return false;
  for (const prop in source) {
    if (!source.hasOwnProperty(prop)) continue;
    if (prop in target) {
      if (typeof target[prop] !== 'object') {
        target[prop] = source[prop];
      } else {
        if (typeof source[prop] !== 'object') {
          target[prop] = source[prop];
        } else {
          if (target[prop].concat && source[prop].concat) {
            target[prop] = target[prop].concat(source[prop]);
          } else {
            target[prop] = deepMerge(target[prop], source[prop]);
          }
        }
      }
    } else {
      target[prop] = source[prop];
    }
  }
  return target;
}